package com.coder4j.starter.web.jwt;


import com.coder4j.starter.tools.util.*;

import java.util.HashMap;
import java.util.Map;

/**
 * Token工具类，负责签发、校验Token
 * @author Keller
 * @date 2021-08-12 20:35:09
 */
public class JWTUtils {

    /**
     * 默认参数
     */
    private static final String PARAMS = "KellerBody";

    /**
     * 获取Header部分
     * @param header
     * @param key
     * @return
     */
    private static String getHeaderStr(JWTHeader header, String key) {
        String headerStr =  Base64Utils.encodeBase64(header.getId()) + "," +
                Base64Utils.encodeBase64(header.getType()) + "," +
                Base64Utils.encodeBase64(header.getTo()) + "," +
                Base64Utils.encodeBase64(header.getCreator()) + "," +
                header.getCreateTime() + "," +
                header.getEffectiveTime() ;
        return AESUtils.encodeAES(headerStr,key);
    }

    /**
     * 解析Header
     * @param headerStr
     * @param key
     * @return
     */
    private static JWTHeader parseHeader(String headerStr, String key){
        if(StringUtils.isEmpty(headerStr)){
            return null;
        }
        headerStr = AESUtils.decodeAES(headerStr,key);
        if(StringUtils.isEmpty(headerStr)){
            return null;
        }
        String[] array = headerStr.split(",");
        if(array.length != 6){
            return null;
        }
        JWTHeader header = new JWTHeader();
        header.setId(Base64Utils.decodeBase64(array[0]));
        header.setType(Base64Utils.decodeBase64(array[1]));
        header.setTo(Base64Utils.decodeBase64(array[2]));
        header.setCreator(Base64Utils.decodeBase64(array[3]));
        header.setCreateTime(Long.parseLong(array[4]));
        header.setEffectiveTime(Integer.parseInt(array[5]));
        return header;
    }

    /**
     * 获取参数部分
     * @param params
     * @param key
     * @return
     */
    private static String getParamStr(Map<String, String> params,String key) {
        if (params == null || params.isEmpty()) {
            return AESUtils.encodeAES(PARAMS,key);
        }
        StringBuffer buffer = new StringBuffer();
        for (String paramName : params.keySet()) {
            buffer.append(Base64Utils.encodeBase64(paramName))
                    .append(":")
                    .append(Base64Utils.encodeBase64(params.get(paramName)))
                    .append(",");
        }
        String paramStr = buffer.substring(0, buffer.length() - 1);
        return AESUtils.encodeAES(paramStr,key);
    }

    /**
     * 解析参数
     * @param paramStr
     * @param key
     * @return
     */
    private static Map<String,String> parseParams (String paramStr,String key){
        if(ObjectUtils.hasEmpty(paramStr,key)){
            return null;
        }
        paramStr = AESUtils.decodeAES(paramStr, key);
        if(StringUtils.isEmpty(paramStr)){
            return null;
        }
        Map<String,String> params = new HashMap<>();

        if(PARAMS.equals(paramStr)){
            return params;
        }

        for (String str : paramStr.split(",")) {
            if(StringUtils.isEmpty(str)){
                continue;
            }
            String[] array = str.split(":");
            if(array.length != 2){
                continue;
            }
            params.put(Base64Utils.decodeBase64(array[0]),Base64Utils.decodeBase64(array[1]));
        }
        return params;
    }

    /**
     * 获取简单Token
     * @return
     */
    public static String getSimpleToken(String key,String to) throws TokenException{
        return getToken(null,null,to,null,key,0,null);
    }

    /**
     * 获取Token
     * @param key
     * @param effectiveTime
     * @param params
     * @return
     * @throws TokenException
     */
    public static String getToken(String key,String to,int effectiveTime,Map<String, String> params) throws TokenException{
        return getToken(null,null,to,null,key,effectiveTime,params);
    }

    /**
     * 获取自定义Token
     * @param key
     * @param header
     * @param params
     * @return
     */
    public static String getCustomToken(String key, JWTHeader header, Map<String,String> params) throws TokenException{
        if(header == null){
            throw TokenException.BuildFaied();
        }
        return getToken(header.getId(),header.getType(),header.getTo(),header.getCreator(),key,header.getEffectiveTime(),params);
    }

    private static String getToken(String id, String type, String to,String creator,String key, int effectiveTime, Map<String, String> params) throws TokenException{
        if (StringUtils.isEmpty(key)) {
            throw TokenException.NoKey();
        }
        JWTHeader header = new JWTHeader();
        if (!StringUtils.isEmpty(id)) {
            header.setId(id);
        }
        if (!StringUtils.isEmpty(type)) {
            header.setType(type);
        }
        if (!StringUtils.isEmpty(to)) {
            header.setTo(to);
        }
        if (!StringUtils.isEmpty(creator)) {
            header.setCreator(creator);
        }
        if (effectiveTime > 0) {
            header.setEffectiveTime(effectiveTime);
        }
        String headerStr = getHeaderStr(header,key);
        String paramStr = getParamStr(params,key);
        String signStr = Md5Utils.getMd5String(headerStr + paramStr);

        if(ObjectUtils.hasEmpty(headerStr,paramStr,signStr)){
            throw TokenException.BuildFaied();
        }

        return headerStr + "." + paramStr + "." + signStr;
    }

    public static JWT parseToken(String tokenStr, String key) throws TokenException{
        if(StringUtils.isEmpty(tokenStr)){
            throw TokenException.Invalid();
        }
        if(StringUtils.isEmpty(key)){
            throw TokenException.NoKey();
        }
        String[] arrays = tokenStr.split("\\.");
        if(arrays.length != 3){
            throw TokenException.Invalid();
        }

        JWT token = new JWT();

        String headerStr = arrays[0];

        JWTHeader header = parseHeader(headerStr,key);
        //Header信息解析失败
        if(header == null){
            throw TokenException.ParseFailed();
        }
        //Token过期
        if(System.currentTimeMillis() > header.getCreateTime() + header.getEffectiveTime() * 3600 * 1000){
            throw TokenException.Invalid();
        }

        String paramStr = arrays[1];
        Map<String,String> params = parseParams(paramStr,key);
        //参数解析失败
        if(params == null){
            throw TokenException.ParseFailed();
        }
        String signStr = arrays[2];
        //验签
        String signCheck = Md5Utils.getMd5String(headerStr + paramStr);
        //签名被篡改
        if(signCheck == null || !signCheck.equals(signStr)){
            throw TokenException.SignError();
        }

        token.setHeader(header);
        token.setParams(params);
        return token;
    }

    public static void main(String[] args) throws TokenException{
        String key = "kellerasdfasdfasdf";

        String to = "张佳琦";
        String tokenStr = getSimpleToken(key,to);
        Console.println("tokenStr",tokenStr);
        JWT token = parseToken(tokenStr, key);
        Console.println("token",token);


        Map<String,String> params = new HashMap<>();
        params.put("name","Keller");
        params.put("role","Admin");
        int time= 12;

        tokenStr = getToken(key,to,time,params);
        Console.println("tokenStr",tokenStr);
        token = parseToken(tokenStr, key);
        Console.println("token",token);
    }
}
